package core.reports;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;

import core.Amount;
import core.Container;
import core.Item;
import core.ItemManager;
import core.Product;
import core.ProductGroup;
import core.ProductManager;
import core.RootStorageUnit;
import core.StorageUnit;
import core.exception.InvalidConversionException;

/**
 * This implementation of IReportVisitor generates the Notices Report
 * @author Royce
 *
 */
public class NoticesVisitor implements IReportVisitor {
	private IReportBuilder _builder;
	private TraversingType _type;
	private ArrayList<Item> _items;
	private StorageUnit _currentSU;
	private Map<Product, ProductGroup> _map;
	private boolean _addedTitle;
	
	public NoticesVisitor(IReportBuilder builder) throws IOException
	{
		setBuilder(builder);
		_type = TraversingType.PREORDER;
		_items = new ArrayList<Item>();
		_currentSU = null;
		_addedTitle = false;
	}
	
	/**
	 * Sets the report Builder that this visitor will use.
	 * @param builder any implementation of IReportBuilder.
	 * @throws IOException 
	 */
	private void setBuilder(IReportBuilder builder) throws IOException {
		_builder = builder;
		//_builder.addParagraph("3-Month Supply Warnings", true);
		//_builder.startTable("3-Month Supply Warnings", 0);
	}

	/**
	 * Called when the Root Storage Unit is visited.
	 * @param rsu the RootStorageUnit.
	 */
	@Override
	public void visitRoot(RootStorageUnit rsu) {
		//Do nothing
	}

	/**
	 * Called when a Storage Unit is visited
	 * @param su a reference to the visited StorageUnit
	 */
	@Override
	public void visitStorageUnit(StorageUnit su) {
		_currentSU = su;
	}

	/**
	 * Called when a Product Group is visited
	 * @param pg a reference to the visited ProductGroup
	 */
	@Override
	public void visitProductGroup(ProductGroup pg) {
		if (pg.getThreeMonthSupply().getQuantity() == 0)
			return;
		Amount currentSupply = new Amount(0, pg.getThreeMonthSupply().getUnit());
		TreeSet<Product> conflicts = new TreeSet<Product>();
		_map = new HashMap<Product, ProductGroup>();
		getProductsRecursive(pg);
		for (Product curP : _map.keySet()) {
			try 
			{
				currentSupply.add(curP.getSize());
			} 
			catch (InvalidConversionException e) 
			{
				conflicts.add(curP);
			}
		}
		if (conflicts.size() != 0)
		{
			try {
				if(!_addedTitle) {
					_builder.addParagraph("3-Month Supply Warnings", true);
					_addedTitle = true;
				}
				_builder.addParagraph("", false);
				StringBuilder sb = new StringBuilder();
				sb.append("Product group " + _currentSU.getName() + "::" +
						pg.getName() + " has a 3-month supply (" + 
						getAmountString(pg.getThreeMonthSupply()) +
						") that is inconsistent with the following products:\n");
				for (Product p: conflicts)
				{
					sb.append("-" + _map.get(p).getName() + "::" + p.getDescription() + 
							"(size: " + getAmountString(p.getSize()) + ")\n");
				}
				_builder.addParagraph(sb.toString(), false);
			} 
			catch (IOException e)
			{
				//do nothing for now
			}
			finally
			{
				_items = new ArrayList<Item>();
			}
		}	
	}
	
	private void getProductsRecursive(ProductGroup p) {
		
		if (p == null) return;

		Iterator<Product> productIter = p.getProducts();
		if(productIter != null) {
			while (productIter.hasNext()) {
				_map.put(productIter.next(), p);
			}
		}
		
		Iterator<Container> cIter = p.getChildContainers();
		if (cIter == null) return;
		while (cIter.hasNext()) {
			getProductsRecursive((ProductGroup)cIter.next());
		}
	}

	private String getAmountString(Amount amt)
	{
		DecimalFormat df = new DecimalFormat("#0.##");
		String theString = df.format(amt.getQuantity()) + " " + amt.getUnit();
		return  theString;
	}
	
	/**
	 * Called when a Product is visited
	 * @param product a reference to the visited Product
	 */
	@Override
	public void visitProduct(Product product) {
		//do nothing

	}

	/**
	 * Called when an Item is visited
	 * @param item a reference to the visited Item
	 */
	@Override
	public void visitItem(Item item)
	{
		if (!item.isUsed())
		{
			_items.add(item);
		}

	}

	@Override
	public TraversingType getType() {
		return _type;
	}

	@Override
	public void visitProductManager(ProductManager pm) {
		//do nothing
	}

	@Override
	public void visitItemManager(ItemManager im) {
		//do nothing
	}

	@Override
	public void onDone() {
		if(!_addedTitle) {
			try {
				_builder.addParagraph("There are no notices at this time.", false);
				_addedTitle = true;
			} 
			catch (IOException e) {
				//ignore
			}
		}
	}
}
